import hashlib


class FunctionInfo:

    def __init__(self, function_info):
        self.function_info = function_info
        self.body_hash = self.hash_function_body(self.pre_handle_function_body(self.get_body()))

    def get_src_info(self):
        return self.function_info

    def get_start_line_number(self):
        return self.function_info["startLineNumber"]

    def get_end_line_number(self):
        return self.function_info["endLineNumber"]

    def get_library_name(self):
        return self.function_info["library"]

    def get_name(self):
        return self.function_info["name"]

    def get_body(self):
        return self.function_info["body"]

    def get_relative_path(self):
        return self.function_info["library"] + "/" + self.function_info["relativePath"]

    def get_inner_relative_path(self):
        return self.function_info["relativePath"]

    def get_location(self):
        return self.generate_function_location(self)

    def get_body_hash(self):
        return self.body_hash

    def get_unique_key(self):
        return self.generate_unique_key(self)

    def is_anonymous_function(self):
        return self.get_name() == "anonymous"

    def has_same_function_body(self, other):
        return self.body_hash == other.body_hash

    def get_signature(self):
        raise NotImplemented("get_signature")

    def __hash__(self):
        return int(self.body_hash, 16)

    def __eq__(self, other):
        if isinstance(other, FunctionInfo):
            return (self.get_name() == other.get_name() and
                    self.has_same_function_body(other))
        return False

    @staticmethod
    def pre_handle_function_body(code: str):
        raise NotImplementedError("you must implement this method")

    @staticmethod
    def is_effective_code_line_content(line_content: str) -> bool:
        raise NotImplementedError("you must implement this method")

    @staticmethod
    def get_src_function(function_info_list):
        if len(function_info_list) == 0:
            return None
        out_function_index = 0
        min_start_line = function_info_list[0].get_start_line_number()
        max_end_line = function_info_list[0].get_end_line_number()
        for index in range(1, len(function_info_list)):
            cur_function_info = function_info_list[index]
            cur_start_line = cur_function_info.get_start_line_number()
            cur_end_line = cur_function_info.get_end_line_number()
            if cur_start_line <= min_start_line and cur_end_line >= max_end_line:
                out_function_index = index
                min_start_line = cur_start_line
                max_end_line = cur_end_line
        return function_info_list[out_function_index]

    @staticmethod
    def match_function_info(start_line, end_line, function_info_list):
        match_function = []
        index_list = []
        for index, function_info in enumerate(function_info_list):
            function_start_line_number = function_info.get_start_line_number()
            function_end_line_number = function_info.get_end_line_number()
            if start_line < function_start_line_number:
                break
            elif start_line >= function_start_line_number \
                    and end_line <= function_end_line_number:
                match_function.append(function_info)
                index_list.append(index)
        return match_function, index_list

    @staticmethod
    def hash_function_body(body):
        """生成函数体哈希值"""
        return hashlib.md5(body.encode()).hexdigest()

    @staticmethod
    def generate_function_location(function_info):
        connect_str = "@"
        return connect_str.join([function_info.get_name(),
                                 function_info.get_relative_path(),
                                 str(function_info.get_start_line_number),
                                 str(function_info.get_end_line_number)])

    @staticmethod
    def generate_unique_key(function_info):
        connect_str = "@"
        return connect_str.join([function_info.get_name(),
                                 function_info.get_inner_relative_path(),
                                 function_info.get_body_hash()])
